﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Diagnostics;
using GenericLibrary.ExtentionMethods;

namespace DiscreteEventModel.Common {

    using SystemMarshal = System.Runtime.InteropServices.Marshal;

    public class MemoryMappedFileBasedInstanceService<T, TData> : InstanceService<long, T, int>
        where T : class, MemoryMappedFileBasedInstanceService<T, TData>.IManagedObject
        where TData : struct {

        public new interface IManagedObject : InstanceService<long, T, int>.IManagedObject {
            TData Data { get; }
        }

        public class HitRateBasedCachingPolicy : InstanceService<long, T, int>.IInstanceCachingPolicy {

            public int TotalHitCount { get; private set; }

            public int TrackOnReferenced(int hitCount) {
                this.TotalHitCount++;
                return hitCount + 1;
            }

            public bool ResurrectOnFinalizing(int hitCount) {
                double hitRate = (double)hitCount / this.TotalHitCount;
                if(this.TotalHitCount > 1024 && (hitRate < 0.1 || hitRate > 0.9)) {
                    this.TotalHitCount -= hitCount;
                    return false;
                }
                return true;
            }
        }

        private MemoryMappedFile _mappedFile;
        private MemoryMappedViewAccessor _viewAccessor;
        private Func<long, TData, T> _factory;
        public long Capacity { get; private set; }

        public MemoryMappedFileBasedInstanceService(long capacity, string filePath, string mapName, Func<long, TData, T> factory)
            : base(new HitRateBasedCachingPolicy()) {
            this.Capacity = capacity;
            this._factory = factory;
            this._mappedFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Create, mapName, this.Capacity * SystemMarshal.SizeOf(typeof(TData)));
            this._viewAccessor = this._mappedFile.CreateViewAccessor();
        }

        protected override T ActivateObject(long key) {
            TData data;
            this._viewAccessor.Read<TData>(key * SystemMarshal.SizeOf(typeof(TData)), out data);
            return this._factory(key, data);
        }

        protected override void PassivateObject(T obj) {
            TData data = obj.Data;
            this._viewAccessor.Write<TData>(obj.Key * SystemMarshal.SizeOf(typeof(TData)), ref data);
        }

        public override void Dispose() {
            this._viewAccessor.Dispose();
            this._viewAccessor = null;
            this._mappedFile.Dispose();
            this._mappedFile = null;
            this._factory = null;
            base.Dispose();
        }
    }

}
